﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Data.Linq;
using System.Web.Mvc;
using Vit.Common.Extensions;
using Vit.Web.Mvc;
using Vit.Web.Services;
using Vit.Web.Validation;

namespace Vit.Web.Data
{
    public abstract class EntityRuleBase<TContext, TEntity> : IEntityRule<TEntity> 
        where TContext : DataContext, new()
        where TEntity : class, new()
    {
        protected TContext db;
        protected IValidationService validationService;

        public EntityRuleBase(IValidationService validationService) 
            : this(validationService, new TContext())
        {
        }

        public EntityRuleBase(IValidationService validationService, TContext dataContext)
        {
            this.validationService = validationService;
            db = dataContext;
        }

        public abstract Func<TEntity, bool> GetEntityKeyPredict(TEntity obj);

        public virtual ValidationStateDictionary Validate(TEntity obj, DataOperation enumOperation)
        {
            ValidationStateDictionary validationState = new ValidationStateDictionary();
            validationState.Add(typeof(TEntity), validationService.Validate<TEntity>(obj, enumOperation));
            return validationState;
        }

        #region IEntityRule<T> Members

        public virtual TEntity New()
        {
            return new TEntity();
        }

        public virtual IQueryable<TEntity> Query(Func<TEntity, bool> predict)
        {
            IQueryable<TEntity> query = db.GetTable<TEntity>();
            if (predict != null)
                query = query.Where(predict).AsQueryable();
            return query;
        }

        public virtual TEntity FirstOrDefault(Func<TEntity, bool> predict)
        {
            var query = Query(predict);
            return query.FirstOrDefault();
        }

        public virtual TEntity Insert(TEntity obj)
        {
            ValidationStateDictionary validationState = Validate(obj, DataOperation.Insert);

            if (validationState.IsValid)
            {
                var tbl = db.GetTable<TEntity>();
                tbl.InsertOnSubmit(obj);
                db.SubmitChanges();
                return obj;
            }
            else
            {
                throw new ValidationException() { ValidationState = validationState };
            }
        }

        public virtual TEntity Update(TEntity obj)
        {
            ValidationStateDictionary validationState = Validate(obj, DataOperation.Update);

            if (validationState.IsValid)
            {
                var tbl = db.GetTable<TEntity>();
                try
                {
                    tbl.Attach(obj, true);
                }
                catch { }
                db.SubmitChanges();
                return obj;
            }
            else
            {
                throw new ValidationException() { ValidationState = validationState };
            }
        }

        public virtual void Delete(TEntity obj)
        {
            var tbl = db.GetTable<TEntity>();
            var entity = tbl.FirstOrDefault(GetEntityKeyPredict(obj));
            if (entity != null)
            {
                tbl.DeleteOnSubmit(entity);
                db.SubmitChanges();
            }
        }

        public virtual void InsertOnSubmit(TEntity obj)
        {
            var tbl = db.GetTable<TEntity>();
            tbl.InsertOnSubmit(obj);
        }

        public virtual void UpdateOnSubmit(TEntity obj)
        {
            var tbl = db.GetTable<TEntity>();
            tbl.Attach(obj, true);
        }

        public virtual void DeleteOnSubmit(TEntity obj)
        {
            var tbl = db.GetTable<TEntity>();
            var entity = tbl.FirstOrDefault(GetEntityKeyPredict(obj));
            if (entity != null)
            {
                tbl.DeleteOnSubmit(entity);
            }
        }

        public virtual void SubmitChanges()
        {
            db.SubmitChanges();
        }

        #endregion
    }
}
